Skip to content

v4: Cartesian OKLab matcher, sRGB-space ordered dither, flat API#29

Merged
g4bri3lDev merged 20 commits into
mainfrom
rust
Apr 28, 2026
Merged

v4: Cartesian OKLab matcher, sRGB-space ordered dither, flat API#29
g4bri3lDev merged 20 commits into
mainfrom
rust

Conversation

@g4bri3lDev
Copy link
Copy Markdown
Member

@g4bri3lDev g4bri3lDev commented Apr 28, 2026

Summary

Three landed changes from the rust development branch:

  • fix(rust): replace LCH-weighted matcher with Cartesian OKLab (Feature - Replace LCH-weighted color matching with weighted Cartesian OKLab distance #28) — fixes the achromatic-attractor bug where vivid out-of-gamut pixels mapped to black/white instead of the nearest chromatic ink. New match_pixel_oklab uses dist² = dL² + Wab²·(da² + db²) with Wab=1.5 (empirically validated against the regression fixture set).
  • fix(rust): apply Bayer threshold in sRGB space (Feature - ordered dither: apply Bayer threshold in perceptual (sRGB) space #27) — uniform perceptual dot density across the tonal range. Pinned by a new property test (ordered_dither_activity_is_perceptually_uniform): linear-space ratio ≈ 13.5 vs sRGB-space ≈ 4.3 across mid-tone bands.
  • feat(api)!: flatten the public API across all 3 packages. Rust uses a DitherConfig struct with Default; Python forces keyword-only args after color_scheme; JavaScript uses an options object. Adds exposure, saturation, shadows, highlights as orthogonal pre-dither knobs alongside the existing tone and gamut.

Test plan

Closes #27 #28.

- Rust: dither() now takes a DitherConfig struct with Default impl, replacing
  the 6-positional-arg signature. Pre-processing pipeline expands to expose
  exposure, saturation, shadows, and highlights as orthogonal knobs.

- Python: dither_image() arguments are now keyword-only after color_scheme.
  Renames tone_compression→tone and gamut_compression→gamut. Adds exposure,
  saturation, shadows, highlights kwargs.

- JavaScript: ditherImage() takes an options object instead of positional args.
  Mirrors Python's API. Exports DitherOptions type.

- FFI: PyO3 and WASM bindings collapse into a single dither_image function
  that accepts either scheme_id or palette_bytes. Removes the previous
  scheme/palette split.

Co-located changes:
- Adds apply_exposure / adjust_saturation / apply_shadows_highlights and
  oklab_to_rgb to enable the new knobs.
- All test suites updated for the new API; 39 Rust + 74 Python + 31 JS tests
  pass.

BREAKING CHANGE: positional args replaced with kwargs/options/config struct.
Callers must update. See README for migration guide.
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 28, 2026

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment

Thanks for integrating Codecov - We've got you covered ☂️

@g4bri3lDev g4bri3lDev merged commit c21ad61 into main Apr 28, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature - ordered dither: apply Bayer threshold in perceptual (sRGB) space

2 participants